/*******************************************************************************
 * Copyright (c) 2021 TNO/ESI
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *    TNO/ESI - initial API and implementation
 *    Obeo - refactoring
 *******************************************************************************/
package test;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;

import org.eclipse.poosl.xtext.PooslStandaloneSetup;

import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.xtext.validation.CheckMode;
import org.eclipse.xtext.validation.IResourceValidator;
import org.eclipse.xtext.validation.Issue;

import com.google.inject.Inject;
import com.google.inject.Injector;

/**
 * This project is for testing and performance analysis only. The required model
 * files to run the tests are not included. The ModelCreator can create simple
 * model files to have a basic test model as comparison. The PerformanceRunner
 * does the following things: First it runs a testmodel (generated by the
 * modelcreator) to load all the classes for the next runs (so you don't have
 * this performance measured every run. Then it will display times for Parsing,
 * Resolving and validating and the memory footprint after parsing and resolving
 * the model. Resolving and userinteraction can be switched on and off by using
 * the booleans in the top of the performancerunner class.
 * 
 * @author <a href="mailto:arjan.mooij@tno.nl">Arjan Mooij</a>
 */
public class PerformanceRunner {
	static boolean resolve = true;
	static boolean userIntervention = false;
	static String modelFilePath = "D:\\asml\\xxl.poosl";
	// static String modelFilePath = "D:\\asml\\testmodel_1.poosl";

	@Inject
	private IResourceValidator validator;

	private static List<Long> measurements;

	public static void main(String[] args) throws InterruptedException, IOException {
		System.out.print("Setting up test environment.");
		Injector pooslInjector = new PooslStandaloneSetup().createInjectorAndDoEMFRegistration();
		PerformanceRunner runner = pooslInjector.getInstance(PerformanceRunner.class);
		measurements = new ArrayList<Long>();
		System.out.print(".");
		// Warmup: (class-loading)
		runner.run(CheckMode.ALL, "D:\\asml\\testmodel_1.poosl");
		measurements.clear();
		System.out.print(".");
		for (int i = 0; i < 1; i++) {
			// resolve = false;
			runner.run(CheckMode.ALL, modelFilePath);
		}
		Long total = Long.valueOf(0);
		for (Long l : measurements) {
			total += l;
		}
		if (measurements.size() > 0)
			System.out.println(
					"Average time over " + measurements.size() + " runs: " + total / measurements.size() + "ms.");
		if (userIntervention) {
			System.out.println("waiting for input (type run and hit enter)");
			BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
			while (!br.readLine().equals("run")) {

			}
		}
	}

	private void run(CheckMode checkmode, String inputFile) throws InterruptedException, IOException {
		System.gc();
		long memorybefore = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
		if (userIntervention) {
			System.out.println("waiting for input (type run and hit enter)");
			BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
			while (!br.readLine().equals("run")) {

			}
		}
		System.out.print(".");
		ResourceSet resourceSet = new ResourceSetImpl();
		resourceSet.getPackageRegistry().put(org.eclipse.poosl.PooslPackage.eINSTANCE.getNsURI(),
				org.eclipse.poosl.PooslPackage.eINSTANCE);
		long currentTime, currentTime2;
		List<Issue> issues;
		System.out.print(".");
		URI uri = URI.createFileURI(inputFile);
		currentTime = System.currentTimeMillis();
		Resource resource = resourceSet.getResource(uri, true);
		System.out.println();
		currentTime2 = System.currentTimeMillis();
		System.out.print(" parsing took: " + (currentTime2 - currentTime) + "ms ");
		currentTime = System.currentTimeMillis();
		if (resolve) {
			EcoreUtil.resolveAll(resource);
			currentTime2 = System.currentTimeMillis();
			System.out.print(" resolving took: " + (currentTime2 - currentTime) + "ms ");
		}
		long memoryafter = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
		System.out.println("memory usage: " + (((memoryafter - memorybefore) / 1024) / 1024) + "Mb");
		System.gc();
		memoryafter = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();

		System.out.println("memory usage after GC: " + (((memoryafter - memorybefore) / 1024) / 1024) + "Mb");
		System.out.println(".");
		// InstrumentationAgent.getObjectSize(resource);
		System.out.print("Validating: " + inputFile + " in mode: " + checkmode + ": \t");
		if (userIntervention) {
			System.out.println("waiting for input (type run and hit enter)");
			BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
			while (!br.readLine().equals("run")) {

			}
		}
		currentTime = System.currentTimeMillis();
		issues = validator.validate(resource, checkmode, null);
		currentTime2 = System.currentTimeMillis();
		System.out.println((currentTime2 - currentTime) + "ms. Found " + issues.size() + " issues");
		if (userIntervention) {
			System.out.println("waiting for input (type run and hit enter)");
			BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
			while (!br.readLine().equals("run")) {

			}
		}
		measurements.add(currentTime2 - currentTime);
		issues.clear();
	}

}
